package com.zyf.chapter02.creation.prototype;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 替换数据为最新版本，目前仅仅存在增加和更新的需求
 * 需求：系统中的数据要么都是版本 a, 要么都是版本 b，不存在介于老版本与新版本之间的中间状态
 * 存在问题：构建成本过高，其实我们只需要新增或者更新的关键词数据就可以了
 * 解决方案： 拷贝 currentKeywords 数据到 newKeywords中，然后从数据库中只捞出新增或者有更新的关键词
 * v3 版存在问题：由于是浅拷贝，导致了 newkeywords 中存在版本 a 的数据，违反了第一个需求
 * v3 解决方案：使用深拷贝 或者 {@link V5Demo#deepCopy(Object)}
 * v4 版存在问题：深拷贝比较耗时
 * v4 解决方案：先采用浅拷贝节省时间，对于要更新的数据，在使用深拷贝创建新对象
 * @author yifeng
 */
public class V5Demo {
    private HashMap<String, SearchWord> currentKeywords = new HashMap<>();
    private long lastUpdateTime = -1;

    public void refresh() {
        // Shallow copy
        HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywords.clone();

        // 从数据中取出更新时间 > lastUpdateTime 的数据，放入到 currentKeyword 中
        List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
        long maxNewUpdatedTime = lastUpdateTime;
        for (SearchWord searchWord : toBeUpdatedSearchWords) {
            // 更新最新时间
            if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
                maxNewUpdatedTime = searchWord.getLastUpdateTime();
            }
            // 存在则直接删除,再重新 put 以实现深拷贝的目的
            if (newKeywords.containsKey(searchWord.getKeyword())) {
                newKeywords.remove(searchWord.getKeyword());
            }
            newKeywords.put(searchWord.getKeyword(), searchWord);
        }
        lastUpdateTime = maxNewUpdatedTime;
        // 新版本创建好之后，在一次性地将服务版本替换为新版本
        currentKeywords = newKeywords;
    }

    /**
     * 从数据库中取出所有的数据
     * @return
     */
    private List<SearchWord> getSearchWords() {
        return null;
    }

    private List<SearchWord> getSearchWords(long lastUpdateTime) {
        // 从数据库中取出更新时间 > lastUpdateTime 的数据
        return null;
    }

    /**
     * 先将对象序列化，然后再反序列化成新的对象
     * @param object 准备深拷贝的对象
     * @return 新对象
     */
    public Object deepCopy(Object object) {
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(object);

            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            return oi.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
